// 扩展4-3 c++中的多线程-demo3 线程同步
/**
 * 原文链接：https://blog.csdn.net/luoweifu/article/details/46835437
 * 
 * CreateMutex、WaitForSingleObject、ReleaseMutex
 * 
 *从【Demo2】中可以看出，虽然创建的子线程都正常执行起来了，但输出的结果并不是我们预期的效果。我们预期的效果是每输出一条语句后自动换行，但结果却并非都是这样。这是因为在线程执行时没有做同步处理，比如第一行的输出，主线程输出“主线程 ===”后时间片已用完，这时轮到子线程1输出，在子线程输出“线程1 —”后时间片也用完了，这时又轮到主线程执行输出“0”，之后又轮到子线程1输出“0”。于是就出现了“主线程 === 线程1 — 0 0”的结果。

          主线程：cout << “主线程 === ” << i << endl;
          子线程：cout << pThreadData->strThreadName << ” — ” << i << endl;

 * 为避免出现这种情况，我们对线程做一些简单的同步处理，这里我们用互斥量(Mutex)，关于互斥量(Mutex)的概念，请看《编程思想之多线程与多进程(2)——线程优先级与线程安全》一文；更多C++线程同步的处理，请看下一节。

 *在使用互斥量进行线程同步时会用到以下几个函数：

      HANDLE WINAPI CreateMutex(
          LPSECURITY_ATTRIBUTES lpMutexAttributes,        //线程安全相关的属性，常置为NULL
          BOOL                  bInitialOwner,            //创建Mutex时的当前线程是否拥有Mutex的所有权
          LPCTSTR               lpName                    //Mutex的名称
      );
 * 说明： lpMutexAttributes也是表示安全的结构，与CreateThread中的lpThreadAttributes功能相同，表示决定返回的句柄是否可被子进程继承，如果为NULL则表示返回的句柄不能被子进程继承。bInitialOwner表示创建Mutex时的当前线程是否拥有Mutex的所有权，若为TRUE则指定为当前的创建线程为Mutex对象的所有者，其它线程访问需要先ReleaseMutex。lpName为Mutex的名称。

      DWORD WINAPI WaitForSingleObject(
          HANDLE hHandle,                             //要获取的锁的句柄
          DWORD  dwMilliseconds                           //超时间隔
      );
 * 说明：
    WaitForSingleObject的作用是等待一个指定的对象(如Mutex对象)，直到该对象处于非占用的状态(如Mutex对象被释放)或超出设定的时间间隔。除此之外，还有一个与它类似的函数WaitForMultipleObjects，它的作用是等待一个或所有指定的对象，直到所有的对象处于非占用的状态，或超出设定的时间间隔。
    hHandle：要等待的指定对象的句柄。dwMilliseconds：超时的间隔，以毫秒为单位；如果dwMilliseconds为非0，则等待直到dwMilliseconds时间间隔用完或对象变为非占用的状态，如果dwMilliseconds 为INFINITE则表示**无限等待**，直到等待的对象处于非占用的状态。

        BOOL WINAPI ReleaseMutex(HANDLE hMutex);

 *------------------------
 * demo3 线程同步
 * 
 * 输出结果如下:
 * 
    create thread1 success !
    create thread2 success !
    Threads are closed !
    子线程-线程1--- i : 0
    子线程-线程2--- i : 0
    主线程======== i = 0
    子线程-线程1--- i : 1
    子线程-线程2--- i : 1
    主线程======== i = 1
    子线程-线程1--- i : 2
    子线程-线程2--- i : 2
    主线程======== i = 2
    子线程-线程1--- i : 3
    子线程-线程2--- i : 3
    主线程======== i = 3
    子线程-线程1--- i : 4
    子线程-线程2--- i : 4
    主线程======== i = 4
    子线程-线程2--- i : 5
    主线程======== i = 5
    子线程-线程2--- i : 6
    主线程======== i = 6
    主线程======== i = 7
    主线程======== i = 8
    主线程======== i = 9
 * 
 * 
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    create thread1 success !
    create thread2 success !
    Threads are closed ! 
    子线程-线程1--- i : 0
    子线程-线程1--- i : 1
    子线程-线程1--- i : 2
    子线程-线程1--- i : 3
    子线程-线程1--- i : 4
    子线程-线程2--- i : 0
    子线程-线程2--- i : 1
    子线程-线程2--- i : 2
    子线程-线程2--- i : 3
    子线程-线程2--- i : 4
    子线程-线程2--- i : 5
    子线程-线程2--- i : 6
    主线程======== i = 0
    主线程======== i = 1
    主线程======== i = 2
    主线程======== i = 3
    主线程======== i = 4
    主线程======== i = 5
    主线程======== i = 6
    主线程======== i = 7
    主线程======== i = 8
    主线程======== i = 9
*/
// #include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <windows.h>
#include <string.h>

#define T_NAME_SIZE 40
#define SLEEP_INTERVAL 100

// HANDLE -> windows.h
HANDLE g_hMutex = NULL; // 互斥量

// 定义线程数据类型
typedef struct __THREAD_DATA
{
  int num;
  char thread_name[T_NAME_SIZE];
  __THREAD_DATA() : num(0)
  {
    memset(thread_name, 0, T_NAME_SIZE * sizeof(char));
  }
} THREAD_DATA;

// 线程函数
DWORD WINAPI threadProc(LPVOID lpParameter)
{
  // WaitForSingleObject(g_hMutex, INFINITE); // 请求获得一个互斥量锁
  THREAD_DATA *data = (THREAD_DATA *)lpParameter;
  for (int i = 0; i < data->num; i++)
  {
    Sleep(SLEEP_INTERVAL);
    WaitForSingleObject(g_hMutex, INFINITE); // 请求获得一个互斥量锁
    // Sleep(SLEEP_INTERVAL);
    std::cout << "子线程-" << data->thread_name << "--- i : " << i << std::endl;
    ReleaseMutex(g_hMutex); // 释放互斥量锁
  }
  // ReleaseMutex(g_hMutex); // 释放互斥量锁
  return 0L;
}

int main(void)
{
  g_hMutex = CreateMutex(NULL, false, NULL); // 创建一个互斥量
  THREAD_DATA td1, td2;                      // 线程数据
  td1.num = 5;
  strcpy(td1.thread_name, "线程1");
  td2.num = 7;
  strcpy(td2.thread_name, "线程2");

  HANDLE thread1 = CreateThread(NULL, 0, threadProc, &td1, 0, NULL);
  if (thread1 == NULL)
  {
    std::cerr << "Error: create thread1 failed ! " << GetLastError() << std::endl;
    exit(-1);
  }
  else
  {
    std::cout << "create thread1 success !" << std::endl;
  }
  HANDLE thread2 = CreateThread(NULL, 0, threadProc, &td2, 0, NULL);
  if (thread2 == NULL)
  {
    std::cout << "Error: create thread2 failed ! " << GetLastError() << std::endl;
    exit(-1);
  }
  else
  {
    std::cout << "create thread2 success !" << std::endl;
  }

  // 关闭线程
  CloseHandle(thread1);
  CloseHandle(thread2);
  std::cout << "Threads are closed ! " << std::endl;

  // WaitForSingleObject(g_hMutex, INFINITE); // 请求等待一个互斥锁
  for (int i = 0; i < 10; i++)
  {
    Sleep(SLEEP_INTERVAL);
    WaitForSingleObject(g_hMutex, INFINITE); // 请求等待一个互斥锁
    // Sleep(SLEEP_INTERVAL);
    std::cout << "主线程======== i = " << i << std::endl;
    ReleaseMutex(g_hMutex); // 释放互斥锁
  }
  // ReleaseMutex(g_hMutex); // 释放互斥锁
  printf("-------------end------------------\n");
  return 0;
}